#include "capture_thread.h"

// 线程运行函数，负责摄像头采集和数据处理的主逻辑
void CaptureThread::run()
{
#ifdef linux  // 仅在Linux系统下执行
#ifndef __arm__  // 排除ARM架构以外的平台（可能针对特定硬件优化）
    return;
#endif

    int video_fd = -1;                  // 摄像头设备文件描述符
    struct v4l2_format fmt;             // 视频格式结构体，用于设置采集参数
    struct v4l2_requestbuffers req_bufs;// 请求缓冲区结构体
    static struct v4l2_buffer buf;      // 单个缓冲区信息结构体
    int n_buf;                          // 缓冲区索引
    struct buffer_info bufs_info[VIDEO_BUFFER_COUNT];  // 缓冲区信息数组（存储映射地址和大小）
    enum v4l2_buf_type type;            // 缓冲区类型（视频采集）

    // 打开摄像头设备（设备路径由宏VIDEO_DEV定义，默认为"/dev/video1"）
    video_fd = open(VIDEO_DEV, O_RDWR);
    if (0 > video_fd) {
        printf("ERROR: failed to open video device %s\n", VIDEO_DEV);
        return ;
    }

    // 配置视频采集格式
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  // 类型为视频采集
    fmt.fmt.pix.width = 640;                 // 采集宽度640像素
    fmt.fmt.pix.height = 480;                // 采集高度480像素
    fmt.fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;  // 色彩空间为SRGB
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB565;  // 像素格式为RGB565（16位色）

    // 向驱动发送设置格式命令（VIDIOC_S_FMT）
    if (0 > ioctl(video_fd, VIDIOC_S_FMT, &fmt)) {
        printf("ERROR: failed to VIDIOC_S_FMT\n");
        close(video_fd);  // 失败则关闭设备并返回
        return ;
    }

    // 配置缓冲区请求参数
    req_bufs.count = VIDEO_BUFFER_COUNT;     // 缓冲区数量（由宏定义，默认为3）
    req_bufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  // 用于视频采集
    req_bufs.memory = V4L2_MEMORY_MMAP;      // 内存映射方式（用户空间直接访问内核缓冲区）

    // 向驱动请求分配缓冲区（VIDIOC_REQBUFS）
    if (0 > ioctl(video_fd, VIDIOC_REQBUFS, &req_bufs)) {
        printf("ERROR: failed to VIDIOC_REQBUFS\n");
        return ;
    }

    // 初始化缓冲区信息，准备进行内存映射
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  // 缓冲区类型为视频采集
    buf.memory = V4L2_MEMORY_MMAP;           // 内存映射方式
    for (n_buf = 0; n_buf < VIDEO_BUFFER_COUNT; n_buf++) {

        buf.index = n_buf;  // 设置当前要查询的缓冲区索引
        // 查询缓冲区信息（VIDIOC_QUERYBUF），获取内核中缓冲区的地址和大小
        if (0 > ioctl(video_fd, VIDIOC_QUERYBUF, &buf)) {
            printf("ERROR: failed to VIDIOC_QUERYBUF\n");
            return ;
        }

        // 保存缓冲区大小和映射后的用户空间地址
        bufs_info[n_buf].length = buf.length;
        bufs_info[n_buf].start = mmap(
            NULL,               // 让系统自动分配用户空间地址
            buf.length,         // 映射大小（缓冲区大小）
            PROT_READ | PROT_WRITE,  // 读写权限
            MAP_SHARED,         // 共享映射（用户空间修改反映到内核）
            video_fd,           // 摄像头设备文件描述符
            buf.m.offset        // 内核缓冲区偏移量（从查询结果获取）
        );

        // 检查映射是否成功
        if (MAP_FAILED == bufs_info[n_buf].start) {
            printf("ERROR: failed to mmap video buffer, size 0x%x\n", buf.length);
            return ;
        }
    }

    // 将所有缓冲区放入采集队列（供驱动填充数据）
    for (n_buf = 0; n_buf < VIDEO_BUFFER_COUNT; n_buf++) {
        buf.index = n_buf;  // 设置当前要入队的缓冲区索引
        // 入队操作（VIDIOC_QBUF）
        if (0 > ioctl(video_fd, VIDIOC_QBUF, &buf)) {
            printf("ERROR: failed to VIDIOC_QBUF\n");
            return ;
        }
    }

    // 启动视频流采集
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (0 > ioctl(video_fd, VIDIOC_STREAMON, &type)) {
        printf("ERROR: failed to VIDIOC_STREAMON\n");
        return ;
    }

    // 循环采集视频数据（受startFlag控制，为true时持续运行）
    while (startFlag) {

        // 遍历所有缓冲区，取出已填充数据的缓冲区进行处理
        for (n_buf = 0; n_buf < VIDEO_BUFFER_COUNT; n_buf++) {

            buf.index = n_buf;
            // 从队列中取出已就绪的缓冲区（VIDIOC_DQBUF）
            if (0 > ioctl(video_fd, VIDIOC_DQBUF, &buf)) {
                printf("ERROR: failed to VIDIOC_DQBUF\n");
                return;
            }

            // 将缓冲区数据转换为QImage（格式为RGB16，对应RGB565）
            QImage qImage(
                (unsigned char*)bufs_info[n_buf].start,  // 图像数据起始地址
                fmt.fmt.pix.width,                       // 宽度
                fmt.fmt.pix.height,                      // 高度
                QImage::Format_RGB16                     // 像素格式
            );

            /* 是否开启本地显示，开启本地显示可能会导致开启广播卡顿，它们互相制约 */
            if (startLocalDisplay)
                emit imageReady(qImage);  // 发送信号，通知主窗口显示图像

            /* 是否开启广播，开启广播会导致本地显示卡顿，它们互相制约 */
            if (startBroadcast) {
                QUdpSocket udpSocket;  // 创建UDP套接字用于广播

                QByteArray byte;       // 存储图像数据的字节数组
                QBuffer buff(&byte);   // 用于IO操作的缓冲区，关联到byte

                // 将QImage保存为JPEG格式到缓冲区（-1表示使用默认质量）
                qImage.save(&buff, "JPEG", -1);

                // 将图像数据转换为Base64编码（便于网络传输）
                QByteArray base64Byte = byte.toBase64();

                // 通过UDP广播数据（目标地址为广播地址，端口8888）
                udpSocket.writeDatagram(
                    base64Byte.data(),    // 数据指针
                    base64Byte.size(),    // 数据大小
                    QHostAddress::Broadcast,  // 广播地址
                    8888                  // 目标端口
                );
            }

            // 处理完成后，将缓冲区重新放入队列（供驱动再次填充数据）
            if (0 > ioctl(video_fd, VIDIOC_QBUF, &buf)) {
                printf("ERROR: failed to VIDIOC_QBUF\n");
                return;
            }
        }
    }

    // 线程结束前等待一段时间（至少650ms，确保资源释放完成）
    msleep(800);

    // 解除所有内存映射
    for (int i = 0; i < VIDEO_BUFFER_COUNT; i++) {
        munmap(bufs_info[i].start, buf.length);
    }

    // 关闭摄像头设备
    close(video_fd);
#endif
}
